/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * Simple program that performs a global servicediscovery or a targeted
 * servicediscovery.
 *
 * \author Harri Kukkonen
 */

#include <iostream>
#include <cstdlib>

#include <signal.h>

#include "gimi.h"

void printUsage()
{
  std::cout << "This program does a serviceDiscovery for all clients or to a "
      "single target, if one is specified.\n";
  std::cout << "Usage: ./gim_servicediscovery [options] [target]\n";
  std::cout << "Option switches are:\n";
  std::cout << "    -h or -?                Show this help and exit.\n";
  std::cout << "    -t hubaddress:hubport,  Hostname:port for tcpHub, default is asrobo.hut.fi:50002\n";
  std::cout << "    -n name,                Name of this client. Default is gimidiscoverer\n";
  std::cout << "    -o timeout,             Timeout in ms for servicediscovery, default 5000ms\n";
  std::cout << "    -d debugLvl,            Debuglevel, default is 6 which shows only errors\n";
}

volatile bool active = true;
gimi::GIMI* gimipointer;

static void sig_handler(int signumber, siginfo_t *info, void *context)
{
  /* If interrupt signal received, stop loops */
  if (info->si_signo == SIGINT) {
    dPrint(1, "Got SIGINT. Stopping.");
    active = false;
    if (gimipointer != NULL)
      gimipointer->stop();
    else
      dPrint(1, "Could not find GIMI-object to stop. Receive/ping/serviceDiscovery"
          "-methods won't quit until timeout.");
  } else if (info->si_signo == SIGTERM) {
    dPrint(1, "Got SIGTERM. Stopping.");
    active = false;
    if (gimipointer != NULL)
      gimipointer->stop();
    else
      dPrint(1, "Could not find GIMI-object to stop. Receive/ping/serviceDiscovery"
          "-methods won't quit until timeout.");
  } else {
    dPrint(1, "Got signal %d", info->si_signo);
  }

  return;
}

int main(int argc, char **argv)
{
  printf("Servicediscovery with %s\n", GIMI_VERSION_STRING);
    
  //// Option reading variables
  bool paramParseLoop = true;
  int option_char;

  //// Signal handling variables.
  struct sigaction siga;
  
  //// Program options
  std::string name = "gimidiscoverer";
  std::string hubaddress = "asrobo.hut.fi";
  std::string target;
  int hubport = 50002;
  int option_debugLevel = 6;
  int timeout = 5000;
  
  //// Program variables
  gimi::GIMI *gi;
  gimi::GIMIMessage message;
  gimi::t_serviceList serviceList;
  int result;

  //// Option reading loop
  while (paramParseLoop) {
    option_char = getopt(argc, argv, "?ht:n:o:d:");
    switch (option_char) {
      case '?':
        printUsage();
        exit(1);
        break;
      case 'h':
        printUsage();
        exit(1);
        break;
      case 'n':
        if (optarg != NULL)
          name = optarg;
        break;
      case 'o':
        if (optarg != NULL)
          timeout = atoi (optarg);
        break;
      case 'd':
        if (optarg != NULL)
          option_debugLevel = atoi (optarg);
        break;
      case 't':
        if (optarg != NULL) {
          std::string optionstring = optarg;
          std::string tempstring;
          uint loc = optionstring.find(':');
          if (loc == std::string::npos) {
            printf("Invalid parameter for target hub! Using default.\n");
          } else if (loc == 0) {
            tempstring = optionstring.substr(loc+1);
            if (tempstring.length() > 0) {
              hubport = atoi(tempstring.c_str());
            }
          } else {
            hubaddress = optionstring.substr(0, loc);
            tempstring = optionstring.substr(loc+1);
            if (tempstring.length() > 0) {
              hubport = atoi(tempstring.c_str());
            }
          }
        }
        break;
      case -1:
        paramParseLoop = false;
        break;
    }
  }
  if (optind < argc) { // Parse arguments that are not options
    target = argv[optind++];
  }

  //// Signal-handling
  sigemptyset(&siga.sa_mask);
  siga.sa_flags = SA_SIGINFO;
  siga.sa_sigaction = sig_handler;
  sigaction(SIGINT, &siga, NULL);
  sigaction(SIGTERM, &siga, NULL);

  //// Initialize dPrint
  debugInit();
  debugSetGlobalDebugLvl(option_debugLevel);
  debugSetMode(DEBUG_MODE_TIMESTAMP_HH_MM_SS_MS);
  
  //// Initialize GIMI
  gi = new gimi::GIMI;
  gimipointer = gi; // Set pointer to GIMI-object for signal-handler.

  //// Connect to hub.
  if(!gi->connectToHub(hubaddress, hubport, name)) {
    printf("Connection to hub %s:%d failed. Aborted.\n", hubaddress.c_str(),
           hubport);
    exit(1);
  }
  dPrint(7, "Connected to hub %s:%d.", hubaddress.c_str(), hubport);
  
  if (!target.empty()) { // Send servicediscovery to target.
    printf("Doing servicediscovery to %s with timeout %d ms.\n", target.c_str(), timeout);
    result = gi->serviceDiscoveryToTarget(serviceList, target, timeout);
  } else {
    printf("Doing servicediscovery with timeout %d ms.\n", timeout);
    result = gi->serviceDiscovery(serviceList, timeout);
  }
  
  if (active) {    
    if (result != GIMI_OK) {
      if (!target.empty()) {
        printf("Servicediscovery to %s failed.\n", target.c_str());
      } else {
        printf("Servicediscovery failed.\n");
      }
    } else {
      if (!target.empty()) {
        dPrint(7, "Servicediscovery to %s success.", target.c_str());
        printf("Found services:\n");
      } else {
        dPrint(7, "Servicediscovery success.");
        printf("Found clients and their services:\n");
      }
      for (gimi::t_serviceListConstIterator iter = serviceList.begin();
            iter != serviceList.end();
            ++iter)
      {
        if (iter->majorServiceId == GIMI_PROTOCOL_CLIENT_DESCRIPTION) {
          printf("[0x%08x] %s ::", iter->clientId, (iter->clientName).c_str());
          if (!(iter->serviceDescription).empty()) {
            printf(" %s", (iter->serviceDescription).c_str());
          }
          if (iter->clientVersion.getVersion() != 0) {
            printf(" v.%s",
                   iter->clientVersion.getVersionString().c_str());
          }
          if (!(iter->serviceLocation).empty()) {
            printf(" @ %s", (iter->serviceLocation).c_str());
          }
          printf(" using GIMI version %s\n",
                 ((iter->gimiVersion).getVersionString()).c_str());
        } else {
          printf("[0x%08x] %s :: %s %d",
                 iter->clientId,
                 (iter->clientName).c_str(),
                 (iter->getShortServiceTypeString()).c_str(),
                 iter->majorServiceId);
          if (iter->minorServiceId >= 0) {
            printf(":%d", iter->minorServiceId);
          }
          // Write name & description for datatype if found.
          gimi::DataTypeDefinition dtdef;
          if (gimi::datatypeDefinitions.getDatatype(dtdef, iter->majorServiceId)) {
            printf(" (%s, %s)", dtdef.dataTypeName.c_str(),
                   dtdef.dataTypeDescription.c_str());
          }
          if (!(iter->serviceDescription).empty()) {
            printf(" %s", (iter->serviceDescription).c_str());
          }
          if (!(iter->serviceLocation).empty()) {
            printf(" @ %s", (iter->serviceLocation).c_str());
          }
          printf("\n");
        }
      }
    }
  }

  delete gi;
  debugDeinit();
  return 0;
}
